-- Some helper functions:

-- sprint(...) helper function does QnD
-- mock print(...) to a string:

function sprint(...)
  local oot = ""
  for i,v in ipairs(arg) do
    oot = oot .. tostring(v)
    if i ~= arg.n then
      oot = oot .. "\t"
    end
  end
  oot = oot .. "\r\n"
  return oot
end

-- ordered pairs({})

function opairs(t)
	local a = {}	-- table of indexes to sort
	for n in pairs(t) do table.insert(a, n) end
	table.sort(a)
	local i = 0
	return function ()
		i = i + 1
		if a[i] == nil then return nil
		else return a[i], t[a[i]]
		end
	end
end

function ptable(t)
	for k,v in opairs(t) do
		print(k,v)
	end
end

function __FILE__() return debug.getinfo(2,'S').source end
function __LINE__() return debug.getinfo(2, 'l').currentline end

--[[ Split a file name into directory, file, and extension.
	 If a component is missing (has no extention etc.) returns
	 that component as the empty string == "".

	 Defaults to normal '/' directory separator but you can
	 pass something else as the optional second argument.
]]

function splitfname(s, dirsep)

	local function findlast(s, c)
		local m = 0
		local n = s:find(c)
		while n do
			m = m + n
			n = s:sub(m + 1):find(c)
		end
		return m
	end

	local dirsep = dirsep or "/"
	local ds = findlast(s, dirsep)
	local es = findlast(s, "%.")	-- we can use Lua patterns

	if ds < es then
		local d = s:sub(1, ds)
		local f = s:sub(ds + 1, es - 1)
		local e = s:sub(es)

		return d, f, e
	else
		local d = s:sub(1, ds)
		local f = s:sub(ds + 1)

		return d, f, ""
	end

	return s, "", ""
end

-- iterate over all tracks, possibly including master

function ialltrax(bmaster)
	local bmaster = bmaster or false
	local nt = reaper.CSurf_NumTracks(bmaster)
	local idx = 0	-- start at one
	if bmaster == true then idx = -1 end	-- start at zero
	return function()
		idx = idx + 1
		if idx <= nt then
			return reaper.CSurf_TrackFromID(idx,bmaster)
		end
	end
end

--[[
	trax(optional selornam, optional incmaster)

	Pick Tracks, possibly including the master, and return
	them in a table.

	If selornam is not passed or is false, return all tracks
	If selornam is true, return all selected tracks

	If selornam is a string, return all tracks with a name
	that includes the string (regardless of selection-state)

	If selornam is a table, return all tracks with names or
	numbers in the list (again regardless of selection-state)

	If incmaster is not passed or is false, do not include the master track;
	if incmaster is true, include the master track in the search.
]]

function trax(selornam, incmaster)

	-- boolean selection
	local function btrax(onlysel, bmaster)
		local all = not(onlysel or false)
		local trt = {}
		for mt in ialltrax(bmaster) do
			local sel = reaper.MediaTrackInfo.GetSelected(mt) == 1
			if all or sel then
				table.insert(trt, mt)
			end
		end
		return trt
	end

	-- string selection (ignore case!)
	local function strax(str, bmaster)
		local trt = {}
		for mt in ialltrax(bmaster) do
			local ns = reaper.MediaTrackInfo.GetName(mt) or "MASTER"
			if string.find(ns:upper(), str:upper()) then
				table.insert(trt, mt)
			end
		end
		return trt
	end

	-- single number selection
	local function ntrax(trn, bmaster)
		local bmaster = bmaster or false
		return reaper.CSurf_TrackFromID(trn, bmaster)
	end

	-- list selection
	local function ltrax(selornam, bmaster)
		local trt = {}
		local atb = {}
		for k,v in opairs(selornam) do
			if type(v) == "string" then
				atb = strax(v, bmaster)
			elseif type(v) == "number" then
				atb = { ntrax(v, bmaster) }
			end
			for l,w in opairs(atb) do
				table.insert(trt, w)
			end
		end
		return trt
	end

	-- determine the flavor of function to call
	if type(selornam) == "boolean" then
		return btrax(selornam, incmaster)
	elseif type(selornam) == "nil" then
		return btrax()
	elseif type(selornam) == "string" then
		return strax(selornam, incmaster)
	elseif type(selornam) == "table" then
		return ltrax(selornam, incmaster)
	end
end

-- http://lua-users.org/wiki/HexDump

function hex_dump(buf)
  for byte=1, #buf, 16 do
	 local chunk = buf:sub(byte, byte+15)
	 io.write(string.format('%08X  ',byte-1))
	 chunk:gsub('.', function (c) io.write(string.format('%02X ',string.byte(c))) end)
	 io.write(string.rep(' ',3*(16-#chunk)))
	 io.write(' ',chunk:gsub('%c','.'),"\n")
  end
end


-- since addMenuCommand is text-based we can set up
-- commands that are not yet loaded

reaper.addMenuCommand(	"TracksTable2HtmlFile(TracksTable())",
						"Save Tracks Table")

-- Only after the next lines are
-- TracksTable2HtmlFile() && TracksTable() defined

reaper.realua.luadir = reaper.GetExePath() .. "\\Plugins\\lua"
lfs.chdir(reaper.realua.luadir)
dofile("examples.lua")

-- I have *had it* trying to get the damned
-- registry to actually change the App Paths
-- So we are forced to write cheat loaders.

-- This cheat loader works for libraries under
-- the lua\lib directory:

local function cheatload(modname)
	local odir = lfs.currentdir()
	lfs.chdir(reaper.realua.luadir .. "\\lib\\" .. modname)
	local loader,msg = package.loadlib(modname, "luaopen_" .. modname)
	if loader then
		loader()
	else
		_G[modname] = nil
		package.loaded[modname] = nil
		print("Cannot loadlib " .. modname .. " " .. msg)
	end
	lfs.chdir(odir)
end

package.preload["curl"] = cheatload


package.preload["wx"] = function(modname)
	local op = print
	cheatload(modname)
	print = op
end

function instlibs()
	window()
	lfs.chdir(reaper.realua.luadir)
	dofile("instlibs.lua")
end

